import json
import time
from datetime import datetime, timedelta
import uuid
import base64
import requests
import logging
import traceback
import sys
from urllib.parse import quote_plus
import rsa

# 导入官方支付宝SDK
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.util.SignatureUtils import verify_with_rsa

# 由于无法直接导入 ParamConstants 中的常量，我们在这里直接定义
# 签名类型常量
ALIPAY_SIGN_TYPE_RSA2 = "RSA2"
ALIPAY_SIGN_TYPE_RSA = "RSA"
# 字符集常量
ALIPAY_CHARSET_UTF8 = "utf-8"

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# 全局配置缓存，避免频繁读取数据库，设置简单的TTL过期时间
_alipay_config_cache = None
_alipay_config_last_fetched = 0
CONFIG_CACHE_TTL = 300  # 5分钟

def get_alipay_config_from_db():
    """
    获取支付宝配置信息。
    当前使用硬编码的配置值，app_id已设置。
    如果需要，您可以将此函数替换为从数据库或安全配置文件获取配置的实际逻辑。
    """
    global _alipay_config_cache, _alipay_config_last_fetched
    current_time = time.time()

    if _alipay_config_cache and (current_time - _alipay_config_last_fetched < CONFIG_CACHE_TTL):
        logger.debug("使用缓存的支付宝配置。")
        return _alipay_config_cache

    logger.info("获取支付宝配置(使用提供的密钥和本地URL)...")
    
    # 使用单行格式的密钥，与JS SDK类似
    app_private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCcKTY8a4vAP0Bk
aa95LeKy0G+5mpNW5JDtD83ESxuP0xnKtmCKPG3kSHvG9oo89r4arv6xXUSV54A5
jVtCb5G0u4vjGLckSgF7PkVAdOxSQOMNbPiUrwnwDSwCJkuoNjn1YT9bhivkEl5v
FgDD48XLunuMZ/UprRInGrjHiqwkF/EmHdfvh7Wz0KvSWSsBVQR1RlHK+c21sAtC
zZt1kUsFeBK1p0GjI90DH+tClh+1rjKThOPFNtPdBfGGV3Jnvv2sMnHvbDwN9SSX
Wm0vE+TxOK3s+bflxGIbJHMGsxiZpr1wB5khk26qk3jhhLrNlho4g8ryflj2f7SX
2ZYhpJo7AgMBAAECggEBAINgqKBsWkaC8EmKIHZyhCar9VLRGOQhp0vJ3/MkvStj
Dm/cv5uM+2bzFC9vex2O+KC6WMwfC3SSLu48XmqmMo86uZV1VZ7SJCzrVpfDxo3s
yRiidP+hw1qRulSUo1IBTQGDrynw1zwpuAj9Geez2NRtL0QQsdE+Ked8M7iskeZK
aIVck8zVJIpUqxVkd/GlfsEnmWZBpoOy2I26V6wu77WPmokKTiw2iOzXksLIulKz
vcyvc4eOE4ZS64O/WCHbBRtecDo1pyScNpbVA9MD4Tu80y/j7xUJMSN+KY0KNOmF
TVT5UAxwa1+fQszhMGGtv3UKVw/phU087q+JSpFm7gECgYEA7pbcExMDiIj5kGmd
F2SI/bBMq/HgKIYd8xmfGkZTjg+1WxWBfDU2/V4CNJzCqGZ9bl8jBBW5Y9vnCVyJ
vIFDv1Uz7iTCwwWhkecuzDgxtsI4hJk6+xZlQNIarikzx9dSPy78x/hzKb6PkONw
KGlmctKL0W2m9K4wU9gfBVKfgEMCgYEAp459h3R4W6y6oRUpXj6cQblbSfZkni2F
e46SnwS3l1lPpKRJBmbrljtTgCVTs81EmWmDAXRZNRKffh80P6Bph8gxuGwjmQDh
yNSqsBUCU+yYYnh8LekcidNLFTGmO93YBvjzd4qXUXoBqpLJ2YwDhVxqBsTgNTnj
O2XgVPTTeqkCgYANVdNBIavt4rIIN073csARZdwzNBayTSS5ZLI21r1u0EPzrlUJ
azQCn1GCPUzH+sqB88FSRiCRi0XziP+N0K/HruxzIAzet57bN8+uzGlC7MIZ7zKT
vCG2qMei+lyRi+Bl9atdRe6x45vEnmGzcIiwXEJCV12giWfcF9VMB90wXwKBgA3e
DSWYzh1IPmn2hmX2aJhH7vZAHjO5MML5GclvaoJsMds+fYy6F+VnI5IHhNXstzC7
Osx0BK4PzINzsa9SZ2mFeBf7Wzbei9FDSJ0qH/cyLap4rk5ebK47fotSgJ+TzL1G
/V93aO6lWl3zqCEJJYF0FJJa2POOxu+zjs15NceBAn8GafNJdUJT2NqiAZqvxebD
VdfUWrRFsJmjCKrvBTZeIgnge4FEv5jVfr+USPczROXd7vnRHoC1OpI1TVHeUfvJ
f1fNkkwAv+Mvmlv9F5G9QtdZEPjMKdXy8otH3Sp1QszRaPVDq0wjO6iVPAOtg3m+
E52I/GY1shUrQMST0W2I
-----END RSA PRIVATE KEY-----"""
    
    alipay_public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmh/T4vt8IYqUyaJ9VqZ
yUAC3jJT3gedDrC2LwjBeLYpZXIKEcC3ddTwFBxoUYrl5Bpziz3BLDGJCqRk4xs1
13VN4e+qwKch1FUsH4pivIOMHZxZq2veamtHowyN2Jch9SvQaXQd0lPXOkVXytNX
0W8vAus2ffe4FxOIGyD4QvuTJqq5JHl8SNE7WK0R12rbHfQRKR364eibgWtX6bcD
UHEHPsIw6lZzNB5UDmTfcRlEO2iQ5PQ/e7LJ/JY19tFp+G5tOmUhBWxK5w0g37nk
z7l4LK4KB9uOh6SCVpsLKEmY4WDq2SQjHnPvztr14Zrxd17Mk+744s+h4q+KTc0S
wwIDAQAB
-----END PUBLIC KEY-----"""
    
    _alipay_config_cache = {
        'app_id': '2021005150647183',
        'app_private_key': app_private_key,  # 使用单行格式的密钥
        'alipay_public_key': alipay_public_key,  # 使用单行格式的密钥
        'notify_url': 'http://localhost:1120/api/payment/alipay/notify/', 
        'return_url': 'http://localhost:1120/api/payment/alipay/return/', 
        'sign_type': ALIPAY_SIGN_TYPE_RSA2,
        'charset': ALIPAY_CHARSET_UTF8,
        'gateway_url': 'https://openapi.alipaydev.com/gateway.do'  # 沙箱环境URL
        # 生产环境使用: 'https://openapi.alipay.com/gateway.do'
    }

    _alipay_config_last_fetched = current_time
    logger.info("已获取支付宝配置，使用完整PEM格式密钥。")
    return _alipay_config_cache

def format_pem_key(pem_key):
    """
    格式化PEM密钥，移除头尾行和换行符
    对于支付宝SDK，密钥需要以正确格式提供
    """
    if not pem_key:
        return None
    
    # 移除开头和结尾的PEM标记行
    lines = pem_key.strip().split('\n')
    if len(lines) > 2:  # 确保有至少3行（头、内容、尾）
        content_lines = lines[1:-1]  # 去掉第一行和最后一行
        return ''.join(content_lines)
    return pem_key  # 如果格式不符合预期，返回原始内容

def get_official_alipay_client_and_config():
    """
    初始化并返回官方DefaultAlipayClient实例
    以及使用的配置字典。
    """
    db_config = get_alipay_config_from_db()
    if not db_config or not db_config.get('app_id'): # 基本检查
        logger.error("无法加载支付宝配置或配置不完整。")
        raise ValueError("支付宝配置缺失或无效。")

    try:
        # 直接使用PEM格式的密钥，不进行任何转换
        formatted_private_key = db_config['app_private_key']
        formatted_public_key = db_config['alipay_public_key']
        
        logger.info("使用完整PEM格式密钥。")

    except Exception as e:
        logger.error(f"处理密钥格式时出错: {str(e)}")
        logger.error(traceback.format_exc())
        raise ValueError(f"处理密钥格式时出错: {str(e)}")

    client_config = AlipayClientConfig()
    client_config.server_url = db_config['gateway_url']
    client_config.app_id = db_config['app_id']
    client_config.app_private_key = formatted_private_key  # 使用格式化为PEM格式的私钥
    client_config.alipay_public_key = formatted_public_key  # 使用格式化为PEM格式的公钥
    
    client_config.sign_type = db_config.get('sign_type', ALIPAY_SIGN_TYPE_RSA2)
    client_config.charset = db_config.get('charset', ALIPAY_CHARSET_UTF8)

    logger.info(f"正在初始化支付宝客户端，App ID: {client_config.app_id}，网关: {client_config.server_url}")
    
    try:
        client = DefaultAlipayClient(alipay_client_config=client_config, logger=logger)
        return client, db_config
    except Exception as e:
        logger.error(f"初始化支付宝客户端失败: {str(e)}")
        logger.error(traceback.format_exc())
        raise ValueError(f"初始化支付宝客户端失败: {str(e)}")

def verify_alipay_notification_signature(params_dict, alipay_config):
    """
    验证支付宝异步通知的签名。

    参数:
        params_dict (dict): 包含支付宝通知参数的字典。
                            此字典应包含'sign'和'sign_type'。
        alipay_config (dict): 包含支付宝配置的字典，需要以下键:
                              'alipay_public_key', 'charset'。
                              将使用params_dict中的'sign_type'值。

    返回:
        bool: 验证成功返回True，否则返回False。
    """
    if not params_dict:
        logger.warning("签名验证失败：未提供参数。")
        return False

    signature = params_dict.pop('sign', None) # 重要：在构建待验证内容前先移除'sign'
    sign_type = params_dict.get('sign_type', ALIPAY_SIGN_TYPE_RSA2) # 使用params_dict中的sign_type，如果存在

    if not signature:
        logger.warning("签名验证失败：在通知参数中找不到'sign'。")
        return False

    # 重建待验证内容：排序键名，过滤None/空值，连接。
    # 'sign'已被移除。'sign_type'通常也从待验证内容字符串中排除。
    params_to_sign_list = []
    for key in sorted(params_dict.keys()):
        value = params_dict[key]
        if value is not None and value != '' and key != 'sign_type': 
            params_to_sign_list.append(f"{key}={value}")
    
    content_to_verify = "&".join(params_to_sign_list)
    
    logger.debug(f"用于签名验证的内容: {content_to_verify}")
    logger.debug(f"来自支付宝的签名: {signature}")
    logger.debug(f"用于验证的签名类型: {sign_type}")
    
    try:
        # 将单行公钥转换为标准PEM格式
        raw_public_key = alipay_config['alipay_public_key']
        chunks = [raw_public_key[i:i+64] for i in range(0, len(raw_public_key), 64)]
        formatted_public_key = "-----BEGIN PUBLIC KEY-----\n" + "\n".join(chunks) + "\n-----END PUBLIC KEY-----"
        logger.debug("成功将支付宝公钥转换为标准PEM格式用于签名验证")
        
        # 使用格式化后的PEM格式公钥进行签名验证
        verified = verify_with_rsa(
            alipay_public_key=formatted_public_key,  # 使用格式化为PEM格式的公钥
            source_content=content_to_verify.encode(alipay_config.get('charset', ALIPAY_CHARSET_UTF8)),
            signature=signature,
            char_set=alipay_config.get('charset', ALIPAY_CHARSET_UTF8),
            sign_type=sign_type
        )

        if verified:
            logger.info("支付宝通知签名验证成功。")
        else:
            logger.warning("支付宝通知签名验证失败。")
        return verified
    except Exception as e:
        logger.error(f"支付宝签名验证过程中出现异常: {str(e)}")
        logger.error(traceback.format_exc())
        return False

# 如果存在，请移除或注释掉以下旧的/自定义的get_alipay_client_from_db函数
# 这个函数应当被替换为上面定义的get_official_alipay_client_and_config函数
def get_alipay_client_from_db(conn, environment='prod'):
    """
    从数据库获取支付宝配置并创建客户端
    :param conn: 数据库连接
    :param environment: 环境，默认prod
    :return: AlipayClient实例
    """
    try:
        with conn.cursor() as cursor:
            # 从数据库获取配置
            query = """
            SELECT config_name, config_value FROM payment_config 
            WHERE platform = 'alipay' AND environment = %s AND is_active = 1
            """
            cursor.execute(query, (environment,))
            config_items = cursor.fetchall()
            
            if not config_items:
                logger.error(f"未找到支付宝配置（环境：{environment}）")
                return None
            
            # 转换为配置字典
            config_dict = {item[0]: item[1] for item in config_items}
            
            # 必需的配置
            required_configs = ['app_id', 'app_private_key', 'alipay_public_key']
            
            # 检查必需的配置是否存在
            missing_configs = [config for config in required_configs if config not in config_dict]
            if missing_configs:
                logger.error(f"缺少必需的支付宝配置: {', '.join(missing_configs)}")
                return None
            
            # 创建配置实例
            alipay_config = AlipayConfig(
                app_id=config_dict['app_id'],
                app_private_key=config_dict['app_private_key'],
                alipay_public_key=config_dict['alipay_public_key'],
                notify_url=config_dict.get('notify_url'),
                return_url=config_dict.get('return_url')
            )
            
            # 返回客户端
            return AlipayClient(alipay_config)
            
    except Exception as e:
        logger.error(f"从数据库获取支付宝配置失败: {str(e)}")
        traceback.print_exc()
        return None 